package br.com.intelsys.basics.dao.hiber;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Query;

import br.com.intelsys.basics.dao.QueryManagerMaker;

public class QueryManagerMakerHibernateTemplate implements QueryManagerMaker {
	private final static int DEFAULT_PREVIOUS = -534;
	private final static int MAX_TRY = 3;
	private static final Log log = LogFactory.getLog(DaoHiber.class);

	private final QueryManagerHibernateTemplate queryManager;
	private final String queryName;
	private Map<String, Object> paramMap;
	private Map<String, Object> paramListMap;
	private int maxResults = DEFAULT_PREVIOUS;
	private int previousMaxResults = DEFAULT_PREVIOUS;
	private int offset = DEFAULT_PREVIOUS;
	private int previousOffset = DEFAULT_PREVIOUS;

	public QueryManagerMakerHibernateTemplate(final QueryManagerHibernateTemplate queryManager, final String queryName) {
		this.queryManager = queryManager;
		this.queryName = queryName;
	}

	public QueryManagerMaker maxResults(int maxResults) {
		if (previousMaxResults == DEFAULT_PREVIOUS) {
			previousMaxResults = 0;
		}
		this.maxResults = maxResults;
		return this;
	}

	private void resetHibernateTemplate() {
		if (previousMaxResults != DEFAULT_PREVIOUS) {
			maxResults = previousMaxResults;
			previousMaxResults = DEFAULT_PREVIOUS;
		}
		if (previousOffset != DEFAULT_PREVIOUS) {
			offset = previousOffset;
			previousOffset = DEFAULT_PREVIOUS;
		}
	}

	public QueryManagerMaker offset(int offset) {
		if (offset == DEFAULT_PREVIOUS) {
			offset = 0;
		}
		this.offset = offset;
		return this;
	}

	public QueryManagerMaker param(String name, Object value) {
		if (paramMap == null) {
			paramMap = new HashMap<String, Object>();
		}
		paramMap.put(name, value);
		return this;
	}

	public QueryManagerMaker paramList(String name, @SuppressWarnings("rawtypes") Collection value) {
		return setParamList(name, value);
	}

	public QueryManagerMaker paramList(String name, Object[] value) {
		return setParamList(name, value);
	}

	private QueryManagerMaker setParamList(String name, Object value) {
		if (paramListMap == null) {
			paramListMap = new HashMap<String, Object>();
		}
		paramListMap.put(name, value);
		return this;
	}

	public List getList() {
		int tryes = 0;
		do {
			try {
				if (tryes > 0 && log.isDebugEnabled()) {
					log.debug("getList error (before). New try: " + tryes);
				}
				List<Object> l = executeGetList();
				if (tryes > 0 && log.isDebugEnabled()) {
					log.debug("getList error... IT's OK, now!!!. New try was: " + tryes);
				}
				return l;
			} catch (Throwable e) {
				log.error("getList error. Trying one more time: " + tryes + " (max " + MAX_TRY + ")");
				e.printStackTrace();
			}
		} while (tryes++ < MAX_TRY);
		return new ArrayList<Object>(0);
	}

	@SuppressWarnings("unchecked")
	private List<Object> executeGetList() {
		List<Object> l;
		Query query = queryManager.getSession().getNamedQuery(queryName);
		prepareHibernateTemplate(query);
		if (paramMap != null || paramListMap != null) {
			makeParamAndValueArray(query);
		}
		l = query.list();
		resetHibernateTemplate();
		if (l == null) {
			l = new ArrayList<Object>(0);
		}
		return l;
	}

	private void prepareHibernateTemplate(Query query) {
		if (maxResults != DEFAULT_PREVIOUS) {
			query.setMaxResults(maxResults);
		}
		if (offset != DEFAULT_PREVIOUS) {
			query.setFirstResult(offset);
		}
	}

	public Object getUniqueValue() {
		@SuppressWarnings("rawtypes")
		List list = getList();
		if (list != null && list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

	@SuppressWarnings("rawtypes")
	private void makeParamAndValueArray(Query query) {
		if (paramMap != null) {
			for (String key : paramMap.keySet()) {
				query.setParameter(key, paramMap.get(key));
			}
		}
		if (paramListMap != null) {
			for (String key : paramListMap.keySet()) {
				Object o = paramListMap.get(key);
				if (o != null) {
					if (o instanceof Collection) {
						query.setParameterList(key, (Collection) o);
					} else {
						query.setParameterList(key, (Object[]) o);
					}
				}
			}
		}
	}
}
